/*
TinyXmlStd http://code.google.com/p/tinyxmlstd/
based on TinyXml, www.sourceforge.net/projects/tinyxml
Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)

This software is provided 'as-is', without any express or implied 
warranty. In no event will the authors be held liable for any 
damages arising from the use of this software.

Permission is granted to anyone to use this software for any 
purpose, including commercial applications, and to alter it and 
redistribute it freely, subject to the following restrictions:

1. The origin of this software must not be misrepresented; you must 
not claim that you wrote the original software. If you use this
software in a product, an acknowledgment in the product documentation
would be appreciated but is not required.

2. Altered source versions must be plainly marked as such, and 
must not be misrepresented as being the original software.

3. This notice may not be removed or altered from any source 
distribution.
*/

#include "tinyxmlstd.h"

//#define DEBUG_PARSER
#if defined( DEBUG_PARSER )
#	if defined( DEBUG ) && defined( _MSC_VER )
#		include <windows.h>
#		define TIXML_LOG OutputDebugString
#	else
#		define TIXML_LOG printf
#	endif
#else
	#define TIXML_LOG(x)
#endif

namespace tinyxmlstd
{
	const Char* GetEntity( const Char* pointer, Char* value, int* length, Encoding::Type encoding );

	//const Char* parse(const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);

	const Char* Document_parse(Document* doc, const Char* pointer, ParsingData* prevData, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);

	/* Attribtue parsing starts: next Char past '<'
		returns: next Char past '>'
		*/
	const Char* Element_parse(Document* document, Element* element, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	const Char* Unknown_parse(Unknown* unknown, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type, ErrorStore* es);

	/* Attribtue parsing starts: at the ! of the !--
		returns: next Char past '>'
		*/
	const Char* Comment_parse(Comment* comment, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type, ErrorStore* es);

	/* Attribute parsing starts: first letter of the name
		returns: the next Char after the value end quote
		*/
	const Char* Attribute_parse(Attribute* attribute, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	const Char* Text_parse(Text* text, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	const Char* Declaration_parse(Declaration* declaration, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	/*Reads the "value" of the element -- another element, or text.
	This should terminate with the current end tag.
	*/
	//const Char* Element_readValue(Element* element, const Char* in, ParsingData* prevData, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	const Char* Element_readValue(Document* doc, Element* element, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es);
	
	// Figure out what is at *pointer, and parse it. Returns null if it is not an xml node.
	NodePtr doNodeIdentify(Document* doc, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es, const Char*& parseResult);

	// Table that returs, for a given lead byte, the total number of bytes
	// in the UTF-8 sequence.
	const int kUtf8ByteTable[256] = 
	{
		//	0	1	2	3	4	5	6	7	8	9	a	b	c	d	e	f
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x00
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x10
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x20
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x30
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x40
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x50
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x60
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x70	End of ASCII range
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x80 0x80 to 0xc1 invalid
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0x90 
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0xa0 
		1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	// 0xb0 
		1,	1,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	// 0xc0 0xc2 to 0xdf 2 byte
		2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	2,	// 0xd0
		3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	3,	// 0xe0 0xe0 to 0xef 3 byte
		4,	4,	4,	4,	4,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1,	1	// 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
	};

	bool IsWhiteSpace( Char c ) 
	{ 
		return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); 
	}

	bool IsWhiteSpace( int c )
	{
		if ( c < 256 )
			return IsWhiteSpace( (Char) c );
		return false; // Again, only truly correct for English/Latin...but usually works.
	}

	const Char* GetChar( const Char* pointer, Char* value, int* length, Encoding::Type encoding )
	{
		XmlAssert( pointer );
		if ( encoding == Encoding::Utf8 )
		{
			*length = kUtf8ByteTable[ *((const unsigned char*)pointer) ];
			XmlAssert( *length >= 0 && *length < 5 );
		}
		else
		{
			*length = 1;
		}

		if ( *length == 1 )
		{
			if ( *pointer == '&' )
				return GetEntity( pointer, value, length, encoding );
			*value = *pointer;
			return pointer+1;
		}
		else if ( *length )
		{
			//strncpy( value, pointer, *length ); // lots of compilers don't like this function (unsafe),
			// and the null terminator isn't needed
			for( int i=0; pointer[i] && i<*length; ++i ) {
				value[i] = pointer[i];
			}
			return pointer + (*length);
		}
		else
		{
			// Not valid text.
			return 0;
		}
	}

	// Bunch of unicode info at:
	//		http://www.unicode.org/faq/utf_bom.html
	// Including the basic of this table, which determines the #bytes in the
	// sequence from the lead byte. 1 placed for invalid sequences --
	// although the result will be junk, pass it through as much as possible.
	// Beware of the non-characters in UTF-8:	
	//				ef bb bf (Microsoft "lead bytes")
	//				ef bf be
	//				ef bf bf 

	const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
	const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
	const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;


	// please note:
	// None of these methods are reliable for any language except English.
	// Good for approximation, not great for accuracy.

	int ToLower( int v, Encoding::Type encoding )
	{
		if ( encoding == Encoding::Utf8 )
		{
			if ( v < 128 ) return tolower( v );
			return v;
		}
		else
		{
			return tolower( v );
		}
	}

	void ConvertUTF32ToUTF8( unsigned long input, Char* output, int* length )
	{
		const unsigned long BYTE_MASK = 0xBF;
		const unsigned long BYTE_MARK = 0x80;
		const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };

		if (input < 0x80) 
			*length = 1;
		else if ( input < 0x800 )
			*length = 2;
		else if ( input < 0x10000 )
			*length = 3;
		else if ( input < 0x200000 )
			*length = 4;
		else
		{ *length = 0; return; }	// This code won't covert this correctly anyway.

		output += *length;

		// Scary scary fall throughs.
		switch (*length) 
		{
		case 4:
			--output; 
			*output = (Char)((input | BYTE_MARK) & BYTE_MASK); 
			input >>= 6;
		case 3:
			--output; 
			*output = (Char)((input | BYTE_MARK) & BYTE_MASK); 
			input >>= 6;
		case 2:
			--output; 
			*output = (Char)((input | BYTE_MARK) & BYTE_MASK); 
			input >>= 6;
		case 1:
			--output; 
			*output = (Char)(input | FIRST_BYTE_MARK[*length]);
		}
	}


	int IsAlpha(unsigned char anyByte, Encoding::Type)
	{
		// This will only work for low-ascii, everything else is assumed to be a valid
		// letter. I'm not sure this is the best approach, but it is quite tricky trying
		// to figure out alhabetical vs. not across encoding. So take a very 
		// conservative approach.

		//	if ( encoding == Encoding::Utf8 )
		//	{
		if ( anyByte < 127 )
			return isalpha( anyByte );
		else
			return 1;	// What else to do? The unicode set is huge...get the english ones right.
		//	}
		//	else
		//	{
		//		return isalpha( anyByte );
		//	}
	}


	int IsAlphaNum(unsigned char anyByte, Encoding::Type)
	{
		// This will only work for low-ascii, everything else is assumed to be a valid
		// letter. I'm not sure this is the best approach, but it is quite tricky trying
		// to figure out alhabetical vs. not across encoding. So take a very 
		// conservative approach.

		//	if ( encoding == Encoding::Utf8 )
		//	{
		if ( anyByte < 127 )
			return isalnum( anyByte );
		else
			return 1;	// What else to do? The unicode set is huge...get the english ones right.
		//	}
		//	else
		//	{
		//		return isalnum( anyByte );
		//	}
	}


	class ParsingData
	{
		friend class Document;
	public:
		void stamp( const Char* now, Encoding::Type encoding );

		const FileLocation& getCursor()	{ return mCursor; }

		// Only used by the document!
		ParsingData( const String& start, bool calculateFileLocation, int row, int chr )
			: mCursor(row, chr)
			, mStamp(start)
			, mCalculateFileLocation(calculateFileLocation)
		{
		}

	private:
		FileLocation		mCursor;
		String		mStamp;
		bool mCalculateFileLocation;
	};


	void ParsingData::stamp( const Char* now, Encoding::Type encoding )
	{
		// Do nothing if the tabsize is 0.
		if(mCalculateFileLocation == false)
		{
			return;
		}

		// Get the current row, column.
		int row = mCursor.row;
		int chr = mCursor.character;
		const Char* pointer = mStamp.c_str();
		XmlAssert( pointer );

		while ( pointer < now )
		{
			// Treat pointer as unsigned, so we have a happy compiler.
			const UChar* pU = (const UChar*)pointer;

			// Code contributed by Fletcher Dunn: (modified by lee)
			switch (*pU) {
			case 0:
				// We *should* never get here, but in case we do, don't
				// advance past the terminating null character, ever
				return;

			case '\r':
				// bump down to the next line
				++row;
				chr = 0;
				// Eat the character
				++pointer;

				// Check for \r\n sequence, and treat this as a single character
				if (*pointer == '\n') {
					++pointer;
				}
				break;

			case '\n':
				// bump down to the next line
				++row;
				chr = 0;

				// Eat the character
				++pointer;

				// Check for \n\r sequence, and treat this as a single
				// character.  (Yes, this bizarre thing does occur still
				// on some arcane platforms...)
				if (*pointer == '\r') {
					++pointer;
				}
				break;

			case '\t':
				// Eat the character
				++pointer;

				// Skip to next tab stop
				++chr;
				break;

			case TIXML_UTF_LEAD_0:
				if ( encoding == Encoding::Utf8 )
				{
					if ( *(pointer+1) && *(pointer+2) )
					{
						// In these cases, don't advance the column. These are
						// 0-width spaces.
						if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )
							pointer += 3;	
						else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )
							pointer += 3;	
						else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )
							pointer += 3;	
						else
						{ pointer +=3; ++chr; }	// A normal character.
					}
				}
				else
				{
					++pointer;
					++chr;
				}
				break;

			default:
				if ( encoding == Encoding::Utf8 )
				{
					// Eat the 1 to 4 byte utf8 character.
					int step = kUtf8ByteTable[*((const UChar*)pointer)];
					if ( step == 0 )
						step = 1;		// Error case from bad encoding, but handle gracefully.
					pointer += step;

					// Just advance one column, of course.
					++chr;
				}
				else
				{
					++pointer;
					++chr;
				}
				break;
			}
		}
		mCursor.row = row;
		mCursor.character = chr;
		XmlAssert( mCursor.row >= -1 );
		XmlAssert( mCursor.character >= -1 );
		XmlAssert(pointer);
		mStamp = pointer;
	}


	const Char* SkipWhiteSpace( const Char* pointer, Encoding::Type encoding )
	{
		if ( !pointer || !*pointer )
		{
			return 0;
		}
		if ( encoding == Encoding::Utf8 )
		{
			while ( *pointer )
			{
				const UChar* pU = (const UChar*)pointer;

				// Skip the stupid Microsoft UTF-8 Byte order marks
				if (	*(pU+0)==TIXML_UTF_LEAD_0
					&& *(pU+1)==TIXML_UTF_LEAD_1 
					&& *(pU+2)==TIXML_UTF_LEAD_2 )
				{
					pointer += 3;
					continue;
				}
				else if(*(pU+0)==TIXML_UTF_LEAD_0
					&& *(pU+1)==0xbfU
					&& *(pU+2)==0xbeU )
				{
					pointer += 3;
					continue;
				}
				else if(*(pU+0)==TIXML_UTF_LEAD_0
					&& *(pU+1)==0xbfU
					&& *(pU+2)==0xbfU )
				{
					pointer += 3;
					continue;
				}

				if ( IsWhiteSpace( *pointer ) || *pointer == '\n' || *pointer =='\r' )		// Still using old rules for white space.
					++pointer;
				else
					break;
			}
		}
		else
		{
			while ( *pointer && IsWhiteSpace( *pointer ) || *pointer == '\n' || *pointer =='\r' )
				++pointer;
		}

		return pointer;
	}


	bool StreamWhiteSpace( std::istream * in, String * tag )
	{
		for( ;; )
		{
			if ( !in->good() ) return false;

			int c = in->peek();
			// At this scope, we can't get to a document. So fail silently.
			if ( !IsWhiteSpace( c ) || c <= 0 )
				return true;

			*tag += (Char) in->get();
		}
	}

	bool StreamTo( std::istream * in, int character, String * tag )
	{
		//XmlAssert( character > 0 && character < 128 );	// else it won't work in utf-8
		while ( in->good() )
		{
			int c = in->peek();
			if ( c == character )
				return true;
			if ( c <= 0 )		// Silent failure: can't get document at this scope
				return false;

			in->get();
			*tag += (Char) c;
		}
		return false;
	}

	/* Reads an XML name into the string provided. Returns
	a pointer just past the last character of the name,
	or 0 if the function has an error.
	One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The
	"assign" optimization removes over 10% of the execution time.
	*/
	const Char* ReadName( const Char* pointer, String * name, Encoding::Type encoding )
	{
		// Oddly, not supported on some comilers,
		//name->clear();
		// So use this:
		name->empty();
		XmlAssert( pointer );

		// Names start with letters or underscores.
		// Of course, in unicode, tinyxml has no idea what a letter *is*. The
		// algorithm is generous.
		//
		// After that, they can be letters, underscores, numbers,
		// hyphens, or colons. (Colons are valid ony for namespaces,
		// but tinyxml can't tell namespaces from names.)
		if (    pointer && *pointer 
			&& ( IsAlpha( (unsigned char) *pointer, encoding ) || *pointer == '_' ) )
		{
			const Char* start = pointer;
			while(		pointer && *pointer
				&&	(		IsAlphaNum( (unsigned char ) *pointer, encoding ) 
				|| *pointer == '_'
				|| *pointer == '-'
				|| *pointer == '.'
				|| *pointer == ':' ) )
			{
				//(*name) += *pointer; // expensive
				++pointer;
			}
			if ( pointer-start > 0 ) {
				name->assign( start, pointer-start );
			}
			return pointer;
		}
		return 0;
	}

	// If an entity has been found, transform it into a character.
	const Char* GetEntity( const Char* pointer, Char* value, int* length, Encoding::Type encoding )
	{
		// Presume an entity, and pull it out.
		String ent;
		*length = 0;

		if ( *(pointer+1) && *(pointer+1) == '#' && *(pointer+2) )
		{
			unsigned long ucs = 0;
			ptrdiff_t delta = 0;
			unsigned mult = 1;

			if ( *(pointer+2) == 'x' )
			{
				// Hexadecimal.
				if ( !*(pointer+3) ) return 0;

				const Char* q = pointer+3;
				q = Strchr( q, ';' );

				if ( !q || !*q ) return 0;

				delta = q-pointer;
				--q;

				while ( *q != 'x' )
				{
					if ( *q >= '0' && *q <= '9' )
						ucs += mult * (*q - '0');
					else if ( *q >= 'a' && *q <= 'f' )
						ucs += mult * (*q - 'a' + 10);
					else if ( *q >= 'A' && *q <= 'F' )
						ucs += mult * (*q - 'A' + 10 );
					else 
						return 0;
					mult *= 16;
					--q;
				}
			}
			else
			{
				// Decimal.
				if ( !*(pointer+2) ) return 0;

				const Char* q = pointer+2;
				q = Strchr( q, ';' );

				if ( !q || !*q ) return 0;

				delta = q-pointer;
				--q;

				while ( *q != '#' )
				{
					if ( *q >= '0' && *q <= '9' )
						ucs += mult * (*q - '0');
					else 
						return 0;
					mult *= 10;
					--q;
				}
			}
			if ( encoding == Encoding::Utf8 )
			{
				// convert the UCS to UTF-8
				ConvertUTF32ToUTF8( ucs, value, length );
			}
			else
			{
				*value = (Char)ucs;
				*length = 1;
			}
			return pointer + delta + 1;
		}

		
		Char specialchar;
		std::size_t inputlength;
		if( GetSpecialChar_fromEncoded(pointer, &specialchar, &inputlength) )
		{
			*value = specialchar;
			*length = 1;
			return ( pointer + inputlength );
		}
		else
		{
			// So it wasn't an entity, its unrecognized, or something like that.
			*value = *pointer;	// Don't put back the last one, since we return it!
			//*length = 1;	// Leave unrecognized entities - this doesn't really work.
			// Just writes strange XML.
			return pointer+1;
		}
	}


	// Return true if the next characters in the stream are any of the endTag sequences.
	// Ignore case only works for english, and should only be relied on when comparing
	// to English words: StringEqual( pointer, "version", true ) is fine.
	bool StringEqual( const Char* pointer,
						   const Char* tag,
						   bool ignoreCase,
						   Encoding::Type encoding )
	{
		XmlAssert( pointer );
		XmlAssert( tag );
		if ( !pointer || !*pointer )
		{
			//XmlAssert( 0 );
			return false;
		}

		const Char* q = pointer;

		if ( ignoreCase )
		{
			while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
			{
				++q;
				++tag;
			}

			if ( *tag == 0 )
				return true;
		}
		else
		{
			while ( *q && *tag && *q == *tag )
			{
				++q;
				++tag;
			}

			if ( *tag == 0 )		// Have we found the end of the tag, and everything equal?
				return true;
		}
		return false;
	}

	/* Reads text. Returns a pointer past the given end tag.
	Wickedly complex options, but it keeps the (sensitive) code in one place.
	*/
	const Char* ReadText(	const Char* pointer, 
							   String * text, 
							   bool keepWhitespaceInTag, 
							   const Char* const endTag, 
							   bool caseInsensitive,
							   WhitespaceAction::Type condenseWhiteSpace,
							   Encoding::Type encoding )
	{
		text->clear();
		if (    !keepWhitespaceInTag			// certain tags always keep whitespace
			|| condenseWhiteSpace == WhitespaceAction::Keep)	// if true, whitespace is always kept
		{
			// Keep all the white space.
			while (	   pointer && *pointer
				&& !StringEqual( pointer, endTag, caseInsensitive, encoding )
				)
			{
				int len;
				Char cArr[4] = { 0, 0, 0, 0 };
				pointer = GetChar( pointer, cArr, &len, encoding );
				text->append( cArr, len );
			}
		}
		else
		{
			bool whitespace = false;

			// Remove leading white space:
			pointer = SkipWhiteSpace( pointer, encoding );
			while (	   pointer && *pointer
				&& !StringEqual( pointer, endTag, caseInsensitive, encoding ) )
			{
				if ( *pointer == '\r' || *pointer == '\n' )
				{
					whitespace = true;
					++pointer;
				}
				else if ( IsWhiteSpace( *pointer ) )
				{
					whitespace = true;
					++pointer;
				}
				else
				{
					// If we've found whitespace, add it before the
					// new character. Any whitespace just becomes a space.
					if ( whitespace )
					{
						(*text) += ' ';
						whitespace = false;
					}
					int len;
					Char cArr[4] = { 0, 0, 0, 0 };
					pointer = GetChar( pointer, cArr, &len, encoding );
					if ( len == 1 )
						(*text) += cArr[0];	// more efficient
					else
						text->append( cArr, len );
				}
			}
		}
		if ( pointer ) 
			pointer += Strlen( endTag );
		return pointer;
	}

	const Char* Document_parse(Document* doc, const Char* pointer, ParsingData* prevData, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		if( es ) es->clearError();

		FileLocation mLocation;

		// parse away, at the document level. Since a document
		// contains nothing but other tags, most of what happens
		// here is skipping white space.
		if ( !pointer || !*pointer )
		{
			es->setError( Error::DocumentEmpty, 0, 0, Encoding::Unknown );
			return 0;
		}

		// Note that, for a document, this needs to come
		// before the while space skip, so that parsing
		// starts from the pointer we are given.
		mLocation.clear();
		if ( prevData )
		{
			mLocation = prevData->getCursor();
		}
		else
		{
			mLocation.row = 0;
			mLocation.character = 0;
		}
		ParsingData data( pointer, doc->calculateFileLocation(), mLocation.row, mLocation.character );
		mLocation = data.getCursor();
		doc->setFileLocation(mLocation);

		if ( encoding == Encoding::Unknown )
		{
			// Check for the Microsoft UTF-8 lead bytes.
			const unsigned char* pU = (const unsigned char*)pointer;
			if (	*(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
				&& *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
				&& *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
			{
				encoding = Encoding::Utf8;
				doc->setMicrosoftBom(true);
			}
		}

		pointer = SkipWhiteSpace( pointer, encoding );
		if ( !pointer )
		{
			es->setError( Error::DocumentEmpty, 0, 0, Encoding::Unknown );
			return 0;
		}

		while ( pointer && *pointer )
		{
			NodePtr node = doNodeIdentify(doc, pointer, &data, encoding, whitespaceAction, es, pointer);
			if ( node )
			{
				doc->getChildren().add(node);
			}
			else
			{
				break;
			}

			// Did we get encoding info?
			if (    encoding == Encoding::Unknown
				&& Declaration::Cast(node) )
			{
				DeclarationPtr dec = Declaration::Cast(node);
				const String& enc = dec->getEncoding();
				if ( StringEqual( enc.c_str(), _T("UTF-8"), true, Encoding::Unknown ) )
					encoding = Encoding::Utf8;
				else if ( StringEqual( enc.c_str(), _T("UTF8"), true, Encoding::Unknown ) )
					encoding = Encoding::Utf8;	// incorrect, but be nice
				else 
					encoding = Encoding::Legacy;
			}

			pointer = SkipWhiteSpace( pointer, encoding );
		}

		// Was this empty?
		if ( doc->getChildren().empty() ) {
			es->setError( Error::DocumentEmpty, 0, 0, encoding );
			return 0;
		}

		// All is well.
		return pointer;
	}

	void ErrorStore::setError( Error::Type err, const Char* pError, ParsingData* data, Encoding::Type encoding )
	{	
		// The first error in a chain is more accurate - don't set again!
		if (mErrorId != Error::None)
			return;

		XmlAssert( err > Error::None && err < Error::NumberOfErrorCodes );
		mErrorId = err;

		mErrorLocation.clear();
		if ( data )
		{
			data->stamp( pError, encoding );
			mErrorLocation = data->getCursor();
		}
	}

	NodePtr doNodeIdentify(Document* doc, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es, const Char*& parseResult)
	{
		pointer = SkipWhiteSpace( pointer, encoding );
		if( !pointer || !*pointer || *pointer != '<' )
		{
			return NodePtr();
		}

		pointer = SkipWhiteSpace( pointer, encoding );

		if ( !pointer || !*pointer )
		{
			return NodePtr();
		}

		// What is this thing? 
		// - Elements start with a letter or underscore, but xml is reserved.
		// - Comments: <!--
		// - Decleration: <?xml
		// - Everthing else is unknown to tinyxml.
		//

		const Char* xmlHeader = { _T("<?xml") };
		const Char* commentHeader = { _T("<!--") };
		const Char* dtdHeader = { _T("<!") };
		const Char* cdataHeader = { _T("<![CDATA[") };

		if ( StringEqual( pointer, xmlHeader, true, encoding ) )
		{
			TIXML_LOG( "XML parsing Declaration\n" );
			DeclarationPtr decl(new Declaration());
			parseResult = Declaration_parse(decl.get(), pointer, data, encoding, whitespaceAction, es);
			return decl;
		}
		else if ( StringEqual( pointer, commentHeader, false, encoding ) )
		{
			TIXML_LOG( "XML parsing Comment\n" );
			CommentPtr comment(new Comment());
			parseResult = Comment_parse(comment.get(), pointer, data, encoding, whitespaceAction, es);
			return comment;
		}
		else if ( StringEqual( pointer, cdataHeader, false, encoding ) )
		{
			TIXML_LOG( "XML parsing CDATA\n" );
			TextPtr text(new Text( _T("") ));
			text->setCdata( true );
			parseResult = Text_parse(text.get(), pointer, data, encoding, whitespaceAction, es);
			return text;
		}
		else if ( StringEqual( pointer, dtdHeader, false, encoding ) )
		{
			TIXML_LOG( "XML parsing Unknown(1)\n" );
			UnknownPtr unknown(new Unknown());
			parseResult = Unknown_parse(unknown.get(), pointer, data, encoding, whitespaceAction, es);
			return unknown;
		}
		else if (    IsAlpha( *(pointer+1), encoding )
			|| *(pointer+1) == '_' )
		{
			TIXML_LOG( "XML parsing Element\n" );
			ElementPtr element(new Element(_T("")));
			parseResult = Element_parse(doc, element.get(), pointer, data, encoding, whitespaceAction, es);
			return element;
		}
		else
		{
			TIXML_LOG( "XML parsing Unknown(2)\n" );
			UnknownPtr unknown(new Unknown());
			parseResult = Unknown_parse(unknown.get(), pointer, data, encoding, whitespaceAction, es);
			return unknown;
		}
	}

	const Char* Element_parse(Document* document, Element* element, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		pointer = SkipWhiteSpace( pointer, encoding );
		//Document* document = getDocument();

		if ( !pointer || !*pointer )
		{
			if (es) es->setError( Error::ParsingElement, 0, 0, encoding );
			return 0;
		}

		if ( data )
		{
			data->stamp( pointer, encoding );
			element->setFileLocation(data->getCursor());
		}

		if ( *pointer != '<' )
		{
			if (es) es->setError( Error::ParsingElement, pointer, data, encoding );
			return 0;
		}

		pointer = SkipWhiteSpace( pointer+1, encoding );

		// Read the name.
		String pErr = pointer;

		String name;
		pointer = ReadName( pointer, &name, encoding );
		if ( !pointer || !*pointer )
		{
			if (es)	es->setError( Error::FailedToReadElementName, pErr.c_str(), data, encoding );
			return 0;
		}

		element->setName(name);

		String endTag (_T("</"));
		endTag += name;
		endTag += _T(">");

		// Check for and read attributes. Also look for an empty
		// tag or an end tag.
		while ( pointer && *pointer )
		{
			pErr = pointer;
			pointer = SkipWhiteSpace( pointer, encoding );
			if ( !pointer || !*pointer )
			{
				if (es) es->setError( Error::FailedReadingElementAttributes, pErr.c_str(), data, encoding );
				return 0;
			}
			if ( *pointer == '/' )
			{
				++pointer;
				// Empty tag.
				if ( *pointer  != '>' )
				{
					if (es) es->setError( Error::FailedParsingEmpty, pointer, data, encoding );
					return 0;
				}
				return (pointer+1);
			}
			else if ( *pointer == '>' )
			{
				// Done with attributes (if there were any.)
				// Read the value -- which can include other
				// elements -- read the end tag, and return.
				++pointer;
				pointer = Element_readValue(document, element, pointer, data, encoding, whitespaceAction, es);		// Note this is an Element method, and will set the error if one happens.
				if ( !pointer || !*pointer ) {
					// We were looking for the end tag, but found nothing.
					// Fix for [ 1663758 ] Failure to report error on bad XML
					if (es) es->setError( Error::FailedReadingEndTag, pointer, data, encoding );
					return 0;
				}

				// We should find the end tag now
				if ( StringEqual( pointer, endTag.c_str(), false, encoding ) )
				{
					pointer += endTag.length();
					return pointer;
				}
				else
				{
					if(es) es->setError( Error::FailedReadingEndTag, pointer, data, encoding );
					return 0;
				}
			}
			else
			{
				// Try to read an attribute:
				Attribute attrib;
				pErr = pointer;
				pointer = Attribute_parse(&attrib, pointer, data, encoding, whitespaceAction, es);

				if ( !pointer || !*pointer )
				{
					if (es) es->setError( Error::ParsingElement, pErr.c_str(), data, encoding );
					return 0;
				}

				element->getAttributes().set(attrib.getName(), attrib.getValue());
			}
		}
		return pointer;
	}

	const Char* Element_readValue(Document* doc, Element* element, const Char* p, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		const Char* pointer = p;
		// Read in text and elements in any order.
		const Char* pWithWhiteSpace = pointer;
		pointer = SkipWhiteSpace( pointer, encoding );

		while ( pointer && *pointer )
		{
			if ( *pointer != '<' )
			{
				// Take what we have, make a text element.
				TextPtr textNode(new Text( _T("") ));

				// Special case: we want to keep the white space
				// so that leading spaces aren't removed.
				pointer = Text_parse(textNode.get(), (whitespaceAction==WhitespaceAction::Condense) ? pointer : pWithWhiteSpace,
					data, encoding, whitespaceAction, es);

				if ( !textNode->isOnlyWhitespace() )
					element->getChildren().add( textNode );
			} 
			else 
			{
				// We hit a '<'
				// Have we hit a new element or an end tag? This could also be
				// a Text in the "CDATA" style.
				if ( StringEqual( pointer, _T("</"), false, encoding ) )
				{
					return pointer;
				}
				else
				{
					NodePtr node = doNodeIdentify(doc, pointer, data, encoding, whitespaceAction, es, pointer);
					if ( node )
					{
						element->getChildren().add( node );
					}
					else
					{
						return 0;
					}
				}
			}
			pWithWhiteSpace = pointer;
			pointer = SkipWhiteSpace( pointer, encoding );
		}

		if ( !pointer )
		{
			if (es) es->setError( Error::FailedReadingElementValue, 0, 0, encoding );
		}	
		return pointer;
	}

	const Char* Unknown_parse(Unknown* unknown, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type, ErrorStore* es)
	{
		pointer = SkipWhiteSpace( pointer, encoding );

		if ( data )
		{
			data->stamp( pointer, encoding );
			unknown->setFileLocation(data->getCursor());
		}
		if ( !pointer || !*pointer || *pointer != '<' )
		{
			if (es) es->setError(Error::FailedParsingUnknown, pointer, data, encoding );
			return 0;
		}
		++pointer;
		Stringstream code;

		while ( pointer && *pointer && *pointer != '>' )
		{
			code << *pointer;
			++pointer;
		}

		unknown->setCode(code.str());

		if ( !pointer )
		{
			if (es) es->setError(Error::FailedParsingUnknown, 0, 0, encoding );
		}
		if ( *pointer == '>' )
			return pointer+1;
		return pointer;
	}

	const Char* Comment_parse(Comment* comment, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type, ErrorStore* es)
	{
		comment->setValue(_T(""));

		pointer = SkipWhiteSpace( pointer, encoding );

		if ( data )
		{
			data->stamp( pointer, encoding );
			comment->setFileLocation(data->getCursor());
		}
		const String startTag = _T("<!--");
		const String endTag   = _T("-->");

		if ( !StringEqual( pointer, startTag.c_str(), false, encoding ) )
		{
			if(es) es->setError( Error::FailedParsingComment, pointer, data, encoding );
			return 0;
		}
		pointer += startTag.length();

		// [ 1475201 ] TinyXML parses entities in comments
		// Oops - ReadText doesn't work, because we don't want to parse the entities.
		// pointer = ReadText( pointer, &value, false, endTag, false, encoding );
		//
		// from the XML spec:
		/*
		[Definition: Comments may appear anywhere in a document outside other markup; in addition, 
		they may appear within the document type declaration at places allowed by the grammar. 
		They are not part of the document's character data; an XML processor MAY, but need not, 
		make it possible for an application to retrieve the text of comments. For compatibility, 
		the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity 
		references MUST NOT be recognized within comments.

		An example of a comment:

		<!-- declarations for <head> & <body> -->
		*/

		Ostringstream value;
		// Keep all the white space.
		while (	pointer && *pointer && !StringEqual( pointer, endTag.c_str(), false, encoding ) )
		{
			value << pointer[0];
			++pointer;
		}
		comment->setValue(value.str());
		if ( pointer ) 
			pointer += endTag.length();

		return pointer;
	}


	const Char* Attribute_parse(Attribute* attribute, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		pointer = SkipWhiteSpace( pointer, encoding );
		if ( !pointer || !*pointer ) return 0;

		//	int tabsize = 4;
		//	if ( document )
		//		tabsize = document->TabSize();

		if ( data )
		{
			data->stamp( pointer, encoding );
			attribute->setFileLocation(data->getCursor());
		}
		// Read the name, the '=' and the value.
		const String& pErr = pointer;
		String name;
		pointer = ReadName( pointer, &name, encoding );
		attribute->setName(name);
		if ( !pointer || !*pointer )
		{
			if ( es ) es->setError( Error::FailedReadingElementAttributes, pErr.c_str(), data, encoding );
			return 0;
		}
		pointer = SkipWhiteSpace( pointer, encoding );
		if ( !pointer || !*pointer || *pointer != '=' )
		{
			if (es) es->setError(Error::FailedReadingElementAttributes, pointer, data, encoding );
			return 0;
		}

		++pointer;	// skip '='
		pointer = SkipWhiteSpace( pointer, encoding );
		if ( !pointer || !*pointer )
		{
			if (es) es->setError(Error::FailedReadingElementAttributes, pointer, data, encoding );
			return 0;
		}

		String end;
		const Char SINGLE_QUOTE = '\'';
		const Char DOUBLE_QUOTE = '\"';

		if ( *pointer == SINGLE_QUOTE )
		{
			++pointer;
			end = _T("\'");		// single quote in string
			String value;
			pointer = ReadText( pointer, &value, false, end.c_str(), false, whitespaceAction, encoding );
			attribute->setValue(value);
		}
		else if ( *pointer == DOUBLE_QUOTE )
		{
			++pointer;
			end = _T("\"");		// double quote in string
			String value;
			pointer = ReadText( pointer, &value, false, end.c_str(), false, whitespaceAction, encoding );
			attribute->setValue(value);
		}
		else
		{
			// All attribute values should be in single or double quotes.
			// But this is such a common error that the parser will try
			// its best, even without them.
			Ostringstream value;
			while (    pointer && *pointer											// existence
				&& !IsWhiteSpace( *pointer ) && *pointer != '\n' && *pointer != '\r'	// whitespace
				&& *pointer != '/' && *pointer != '>' )							// tag end
			{
				if ( *pointer == SINGLE_QUOTE || *pointer == DOUBLE_QUOTE ) {
					// [ 1451649 ] Attribute values with trailing quotes not handled correctly
					// We did not have an opening quote but seem to have a 
					// closing one. Give up and throw an error.
					if (es) es->setError(Error::FailedReadingElementAttributes, pointer, data, encoding );
					return 0;
				}
				value << *pointer;
				++pointer;
			}
			attribute->setValue(value.str());
		}
		return pointer;
	}

	const Char* Text_parse(Text* text, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		text->setValue(_T(""));
		
		if ( data )
		{
			data->stamp( pointer, encoding );
			text->setFileLocation(data->getCursor());
		}

		const String startTag = _T("<![CDATA[");
		const String endTag   = _T("]]>");

		if ( text->isCdata() || StringEqual( pointer, startTag.c_str(), false, encoding ) )
		{
			text->setCdata(true);

			if ( !StringEqual( pointer, startTag.c_str(), false, encoding ) )
			{
				if(es) es->setError( Error::ParsingCdata, pointer, data, encoding );
				return 0;
			}
			pointer += startTag.length();

			// Keep all the white space, ignore the encoding, etc.
			Ostringstream value;
			while (	   pointer && *pointer
				&& !StringEqual( pointer, endTag.c_str(), false, encoding )
				)
			{
				value << *pointer;
				++pointer;
			}
			text->setValue(value.str());

			String dummy; 
			pointer = ReadText( pointer, &dummy, false, endTag.c_str(), false, whitespaceAction, encoding );
			return pointer;
		}
		else
		{
			bool ignoreWhite = true;

			const String& end = _T("<");
			String value;
			pointer = ReadText( pointer, &value, ignoreWhite, end.c_str(), false, whitespaceAction, encoding );
			text->setValue(value);
			if ( pointer )
				return pointer-1;	// don't truncate the '<'
			return 0;
		}
	}

	const Char* Declaration_parse(Declaration* declaration, const Char* pointer, ParsingData* data, Encoding::Type encoding, WhitespaceAction::Type whitespaceAction, ErrorStore* es)
	{
		pointer = SkipWhiteSpace( pointer, encoding );
		// Find the beginning, find the end, and look for
		// the stuff in-between.
		
		if ( !pointer || !*pointer || !StringEqual( pointer, _T("<?xml"), true, encoding ) )
		{
			if (es) es->setError(Error::FailedParsingDeclaration, 0, 0, encoding );
			return 0;
		}
		if ( data )
		{
			data->stamp( pointer, encoding );
			declaration->setFileLocation(data->getCursor());
		}
		pointer += 5;

		declaration->setVersion(_T(""));
		declaration->setEncoding(_T(""));
		declaration->setStandalone(_T(""));

		while ( pointer && *pointer )
		{
			if ( *pointer == '>' )
			{
				++pointer;
				return pointer;
			}

			pointer = SkipWhiteSpace( pointer, encoding );
			if ( StringEqual( pointer, _T("version"), true, encoding ) )
			{
				Attribute attrib;
				pointer = Attribute_parse(&attrib, pointer, data, encoding, whitespaceAction, es);
				declaration->setVersion(attrib.getValue());
			}
			else if ( StringEqual( pointer, _T("encoding"), true, encoding ) )
			{
				Attribute attrib;
				pointer = Attribute_parse(&attrib, pointer, data, encoding, whitespaceAction, es);
				declaration->setEncoding(attrib.getValue());
			}
			else if ( StringEqual( pointer, _T("standalone"), true, encoding ) )
			{
				Attribute attrib;
				pointer = Attribute_parse(&attrib, pointer, data, encoding, whitespaceAction, es);
				declaration->setStandalone(attrib.getValue());
			}
			else
			{
				// Read over whatever it is.
				while( pointer && *pointer && *pointer != '>' && !IsWhiteSpace( *pointer ) )
					++pointer;
			}
		}
		return 0;
	}

	bool Text::isOnlyWhitespace() const
	{
		for ( unsigned i=0; i<mValue.length(); i++ )
			if ( !IsWhiteSpace( mValue[i] ) )
				return false;
		return true;
	}
}

